package com.csthink.web.infrastructure.security.authentication.parser;

import com.csthink.common.infrastructure.constant.JwtConstants;
import com.csthink.common.infrastructure.constant.JwtIssuer;
import com.csthink.common.infrastructure.enums.Authorities;
import com.csthink.common.infrastructure.enums.ErrorEnum;
import com.csthink.common.infrastructure.exception.BaseException;
import com.csthink.common.infrastructure.util.AssertUtil;
import com.csthink.common.infrastructure.util.JwtUtils;
import com.csthink.mysql.entity.User;
import com.csthink.web.application.service.UserService;
import com.csthink.web.infrastructure.security.authentication.domain.JwtAuthenticationToken;
import com.csthink.web.infrastructure.security.authorization.domain.PermissionAuthority;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.csthink.common.infrastructure.constant.JwtConstants.AUTHORIZATION;
import static com.csthink.common.infrastructure.constant.JwtConstants.AUTHORIZATION_BEARER;

/**
 * URL默认认证解析器，解析成功后生成 Authentication
 *
 * @author <a href="mailto:security.2009@live.cn">Mars</a>
 * @since 2023-09-12
 */
@Component
@Slf4j
public class DefaultAuthenticationParser extends AbstractAuthenticationParser {

    @Resource
    private UserService userService;

    @Resource
    private JwtUtils jwtUtils;

    private static final List<AntPathRequestMatcher> MATCHERS = List.of(new AntPathRequestMatcher("/**"));

    @Override
    public Authentication getAuthentication(HttpServletRequest request) {
        log.info("\n {} - {}", request.getMethod(), request.getRequestURL().toString());
        String authorizationHeader = request.getHeader(AUTHORIZATION);
        if (StringUtils.isBlank(authorizationHeader) || !authorizationHeader.startsWith(AUTHORIZATION_BEARER)) {
            throw new AuthenticationCredentialsNotFoundException("Authentication header not found.");
        }

        String token = authorizationHeader.substring(AUTHORIZATION_BEARER.length());
        Claims claims = jwtUtils.parseJwt(token);

        checkTokenExpire(claims);
        User user = userService.loadByUsername("222");
        AssertUtil.notNull(user, ErrorEnum.USER_NOT_FOUND);
        // todo 根据user查询出权限scopes
        List<String> scopes = List.of(
                Authorities.CST_USER.editValue(),
                Authorities.CST_INBOX.readValue()
        );

        List<PermissionAuthority> permissionAuthorities = scopes.stream()
                .distinct()
                .map(PermissionAuthority::new)
                .collect(Collectors.toList());

        Authentication authentication = new JwtAuthenticationToken(user, null, permissionAuthorities, token);
        return authentication;
    }

    private void checkTokenExpire(Claims claims) {
        String iss = claims.get(JwtConstants.ISSUE, String.class);
        String aud = claims.get(JwtConstants.AUDIENCE, String.class);
        Date expired = claims.get(JwtConstants.EXPIRE, Date.class);

        if (!StringUtils.equals(iss, JwtIssuer.WEB)) {
            throw new BaseException(ErrorEnum.JWT_TOKEN_INVALID);
        }

        if (!StringUtils.equals(aud, JwtIssuer.WEB)) {
            throw new BaseException(ErrorEnum.JWT_TOKEN_INVALID);
        }

        if (expired.compareTo(new Date()) <= 0) {
            throw new BaseException(ErrorEnum.JWT_TOKEN_EXPIRED);
        }
    }

    @Override
    public List<AntPathRequestMatcher> getTargetUrlMatchers() {
        return MATCHERS;
    }
}
